              package com.java.diagnostics.visualizer.displayer.tabbed;
              
              import com.java.diagnostics.visualizer.coredisplayers.util.Messages;
              import com.java.diagnostics.visualizer.data.DataPoint;
              import com.java.diagnostics.visualizer.data.DataSet;
              import com.java.diagnostics.visualizer.data.TupleData;
              import com.java.diagnostics.visualizer.data.Variant;
              import com.java.diagnostics.visualizer.data.axes.DataAxis;
              import com.java.diagnostics.visualizer.data.axes.UnitConverterDefinition;
              import com.java.diagnostics.visualizer.data.axes.XDataAxis;
              import com.java.diagnostics.visualizer.data.ids.ID;
              import com.java.diagnostics.visualizer.display.AxisSelection;
              import com.java.diagnostics.visualizer.display.SequenceIDAwareDisplayer;
              import com.java.diagnostics.visualizer.exceptions.GCAndMemoryVisualizerException;
              import com.java.diagnostics.visualizer.impl.display.DataDisplayerImpl;
              import com.java.diagnostics.visualizer.impl.factory.LogFactory;
              import com.java.diagnostics.visualizer.impl.selection.GCAndMemoryVisualizerSelectionProvider;
              import com.java.diagnostics.visualizer.parser.vgc.constants.VGCAxes;
              import com.java.diagnostics.visualizer.properties.IDsToDisplayProperties;
              import com.java.diagnostics.visualizer.properties.OutputProperties;
              import java.io.FileWriter;
              import java.io.IOException;
              import java.util.Arrays;
              import java.util.Comparator;
              import java.util.HashMap;
              import java.util.Iterator;
              import java.util.Map;
              import java.util.PriorityQueue;
              import java.util.logging.Logger;
              import org.eclipse.jface.viewers.ISelection;
              import org.eclipse.jface.viewers.ISelectionProvider;
              import org.eclipse.jface.viewers.StructuredSelection;
              import org.eclipse.swt.dnd.Clipboard;
              import org.eclipse.swt.dnd.TextTransfer;
              import org.eclipse.swt.dnd.Transfer;
              import org.eclipse.swt.events.MouseEvent;
              import org.eclipse.swt.events.MouseMoveListener;
              import org.eclipse.swt.events.SelectionEvent;
              import org.eclipse.swt.events.SelectionListener;
              import org.eclipse.swt.graphics.Device;
              import org.eclipse.swt.graphics.GC;
              import org.eclipse.swt.graphics.Point;
              import org.eclipse.swt.graphics.Rectangle;
              import org.eclipse.swt.layout.FillLayout;
              import org.eclipse.swt.widgets.Composite;
              import org.eclipse.swt.widgets.Display;
              import org.eclipse.swt.widgets.Event;
              import org.eclipse.swt.widgets.Listener;
              import org.eclipse.swt.widgets.Menu;
              import org.eclipse.swt.widgets.Shell;
              import org.eclipse.swt.widgets.Table;
              import org.eclipse.swt.widgets.TableColumn;
              import org.eclipse.swt.widgets.TableItem;
              
              public class TabbedDataDisplayer extends DataDisplayerImpl
                implements SequenceIDAwareDisplayer, SelectionListener, MouseMoveListener
              {
                private static final String HEADER = "HEADER";
                private static final int UNSET = -1;
                private static final String COLON = ": ";
                private static final String FILE_EXTENSION = ".txt";
                private static final String FILE_EXTENSION2 = ".csv";
                private static final String WILDCARD = "*";
                private static final String QUOTE = String.valueOf('"');
                private static final String DOUBLEQUOTES = QUOTE + QUOTE;
              
                private static final Logger TRACE = LogFactory.getTrace(TabbedDataDisplayer.class);
              
                private static final String VARIANT = Messages.getString("TextDataDisplayer.variant");
                private Composite composite;
                private ISelectionProvider selectionProvider;
                private DataSet dataSet = null;
                private Table table;
                private int titleRows = 0;
                private Point lastMouse = new Point(0, 0);
                private final Map<String, DataAxis> axes = new HashMap();
                private boolean gcNumberAvailable = false;
                private Shell shell = null;
              
                private static final String UID_LABEL = Messages.getString("TabbedDataDisplayer.unique.id.column.header");
              
                public TabbedDataDisplayer() {
                  this.selectionProvider = new GCAndMemoryVisualizerSelectionProvider();
                }
              
                public void dispose() {
                  if (this.composite != null)
                    this.composite.dispose();
                }
              
                public void print() throws GCAndMemoryVisualizerException
                {
                }
              
                public Object display(DataSet dataSet, OutputProperties outputProperties)
                {
                  TRACE.entering(this.className, "display");
                  this.axes.clear();
                  String key = getClass().getName();
              
                  this.composite = ((Composite)
                    outputProperties.getDisplayerProperties(key));
              
                  if ((this.composite == null) || (this.composite.isDisposed())) {
                    Display display = Display.getCurrent();
                    this.shell = new Shell(display);
              
                    this.composite = new Composite(this.shell, 0);
                    this.composite.setBounds(0, 0, 100, 100);
                  }
              
                  FillLayout layout = new FillLayout();
                  layout.type = 512;
                  this.composite.setLayout(layout);
                  this.composite.layout();
              
                  this.dataSet = dataSet;
              
                  writeToTable(dataSet, outputProperties);
              
                  this.composite.layout();
              
                  TRACE.exiting(this.className, "display");
                  return this.composite;
                }
              
                public void save(String name) throws GCAndMemoryVisualizerException {
                  TRACE.entering(this.className, "save");
                  try {
                    if (name != null) {
                      String tabbedData = getTableData(this.table);
                      String saveName = name;
                      FileWriter writer = new FileWriter(saveName);
                      writer.write(tabbedData);
                      writer.close();
                    }
                  } catch (IOException e) {
                    throw new GCAndMemoryVisualizerException(e);
                  } finally {
                    TRACE.exiting(this.className, "save");
                  }
                }
              
                public void copyToClipboard() {
                  TableItem[] items = this.table.getSelection();
              
                  if ((items == null) || (items.length == 0)) {
                    return;
                  }
                  String text = getTableData(this.table, items);
              
                  if (text.length() > 0) {
                    Clipboard clipboard = new Clipboard(Display.getCurrent());
                    TextTransfer transfer = TextTransfer.getInstance();
                    clipboard.setContents(new Object[] { text.toString() }, 
                      new Transfer[] { transfer });
                  }
                }
              
                public String[] getFileSaveExtensions() {
                  return new String[] { "*.txt", 
                    "*.csv" };
                }
              
                public String getDefaultFileSaveExtension() {
                  return ".txt";
                }
              
                public int compareTo(String s1, String s2) {
                  try {
                    double d1 = Double.parseDouble(s1);
                    double d2 = Double.parseDouble(s2);
                    return Double.compare(d1, d2);
                  } catch (NumberFormatException localNumberFormatException) {
                  }
                  return s1.compareTo(s2);
                }
              
                private void writeToTable(DataSet dataSet, OutputProperties outputProperties)
                {
                  Iterator ids = outputProperties.getTupleFieldsToDisplay()
                    .getAllIDsToDisplay();
              
                  if (this.table != null) {
                    this.table.setMenu(null);
                    this.table.removeSelectionListener(this);
                    this.table.dispose();
                    this.table = null;
                  }
              
                  this.table = new Table(this.composite, 67586);
              
                  this.table.addSelectionListener(this);
                  this.table.addMouseMoveListener(this);
              
                  this.table.setHeaderVisible(true);
                  this.table.setLinesVisible(true);
              
                  Menu menu = this.composite.getMenu();
                  this.table.setMenu(menu);
              
                  int cols = 0;
                  int[] colToUseForX = new int[dataSet.getVariants().length];
              
                  calculateNumTitleRows();
                  Variant[] variants;
                  int v = 0;
                  for (; ids.hasNext(); )
                    
                  {
                    ID id = (ID)ids.next();
                    variants = dataSet.getVariants();
              
/* 241 *///          / v = 0; continue;
                    Variant variant = variants[v];
              
                    TupleData td = variant.getTupleData(id);
              
                    if (td != null)
                    {
                      String originalUnits = outputProperties.getXUnits(td);
              
                      this.gcNumberAvailable = false;
                      String[] availableUnits = td.getXAxis().getPossibleUnits();
                      for (int i = 0; i < availableUnits.length; i++) {
                        if (VGCAxes.GC_NUMBER.equals(availableUnits[i])) {
                          this.gcNumberAvailable = true;
                          break;
                        }
                      }
              
                      if (cols == 0)
                      {
                        TableColumn column = new TableColumn(this.table, 131072);
                        String label = UID_LABEL;
                        column.setText(label);
              
                        column.setResizable(false);
                        column.setWidth(0);
                        cols++;
              
                        column = new TableColumn(this.table, 131072);
              
                        XDataAxis axis = td.getXAxis();
                        String units = outputProperties
                          .getUnits(axis.getAxis());
                        if (this.gcNumberAvailable) {
                          units = VGCAxes.GC_NUMBER;
                        }
                        column.setText(
                          axis.getConverterDefinition(units).getLabel());
                        column.addSelectionListener(new SortingListener());
                        cols++;
              
                        for (int k = 0; k < this.titleRows; k++) {
                          TableItem subtitle = new TableItem(this.table, 0);
                          subtitle
                            .setBackground(this.table
                            .getDisplay()
                            .getSystemColor(
                            22));
                          subtitle.setData("HEADER", new Object());
                        }
              
                        TableItem unittitle = this.table.getItem(0);
                        unittitle.setBackground(
                          this.table.getDisplay().getSystemColor(22));
                        unittitle.setText(1, units);
                      }
              
                      boolean useXCol = 
                        (!VGCAxes.GC_NUMBER
                        .equals(originalUnits)) && 
                        (this.gcNumberAvailable);
              
                      if ((useXCol) && (colToUseForX[v] == 0))
                      {
                        String label = td.getXAxis().getLabel();
                        String units = outputProperties.getXUnits(td);
              
                        TableColumn column = cols >= this.table.getColumnCount() ? new TableColumn(
                          this.table, 131072) : 
                          this.table.getColumn(cols);
                        column.setMoveable(true);
                        column.setToolTipText(VARIANT + ": " + 
                          variant.getLabel());
                        column.addSelectionListener(new SortingListener());
                        colToUseForX[v] = cols;
                        column.setText(label);
                        TableItem unittitle = this.table.getItem(0);
                        unittitle.setText(cols, units);
              
                        addVariantLabels(cols, variant);
                        cols++;
                      }
              
                      addSubheaders(cols, variant);
                    }
                    v++;
                  }
              
                  ids = outputProperties.getTupleFieldsToDisplay().getAllIDsToDisplay();
/*     *///          Variant[] variants;
/*     *///          int v;
                  TupleData td;
                  DataAxis axis;
                  String originalUnits;
                  for (; ids.hasNext(); )
/* 346 *///          *     */     
                	  {
                    ID id = (ID)ids.next();
                    variants = dataSet.getVariants();
              
/* 346 *///          / v = 0; continue;
                    Variant variant = variants[v];
                    td = variant.getTupleData(id);
              
                    if (td != null)
                    {
                      axis = td.getXAxis();
                      originalUnits = outputProperties.getXUnits(td);
                      this.axes.put(td.getXAxis().getLabel(), axis);
              
                      boolean useXCol = 
                        (!VGCAxes.GC_NUMBER
                        .equals(originalUnits)) && 
                        (this.gcNumberAvailable);
              
                      if ((useXCol) && (colToUseForX[v] == 0))
                      {
                        colToUseForX[v] = cols;
                        addVariantLabels(cols, variant);
                        cols++;
                      }
              
                      createYColumn(cols, variant, td, outputProperties);
              
                      addSubheaders(cols, variant);
                      int currentColumn = colToUseForX[v];
              
                      populateColumn(cols, td, outputProperties, useXCol, 
                        currentColumn);
                      cols++;
                    }
                    v++;
                  }
              
                  for (int i = 0; i < this.table.getColumnCount(); i++) {
                    this.table.getColumn(i).pack();
                  }
              
                  if (this.table.getColumnCount() > 0)
                  {
                    this.table.getColumn(0).setWidth(0);
                  }
              
                  if ((this.table.getColumnCount() > 1) && 
                    (this.table.getColumn(1).getText().equalsIgnoreCase(
                    VGCAxes.COLLECTION)))
                  {
                    this.table.getColumn(1).setWidth(0);
                  }
              
                  this.table.addListener(40, new Listener() {
                    public void handleEvent(Event event) {
                      int itemIndex = TabbedDataDisplayer.this.table.indexOf((TableItem)event.item);
                      if (itemIndex < TabbedDataDisplayer.this.titleRows) {
                        GC gc = event.gc;
                        gc.setForeground(gc.getDevice()
                          .getSystemColor(22));
                        gc.setBackground(gc.getDevice()
                          .getSystemColor(18));
                        int gradientHeight = event.height / 7;
                        gc
                          .fillGradientRectangle(event.x, event.y + 
                          event.height - gradientHeight, 
                          event.width, gradientHeight, true);
                        gc.setForeground(gc.getDevice()
                          .getSystemColor(18));
                        gc.setBackground(gc.getDevice()
                          .getSystemColor(22));
                        gc.fillRectangle(event.x, event.y, event.width, 
                          event.height - gradientHeight);
                        int lineX = event.x + event.width - 2;
                        gc.setBackground(gc.getDevice()
                          .getSystemColor(20));
                        gc.fillGradientRectangle(lineX, event.y + gradientHeight, 
                          1, event.height - 2 * gradientHeight, false);
                        event.detail &= -9;
                      }
                    }
                  });
                  if (new TabbedDataPreferenceHelper().getDateFirst()) {
                    int[] order = this.table.getColumnOrder();
                    int[] newOrder = new int[order.length];
                    int i = 0;
                    while (i < colToUseForX.length) {
                      newOrder[i] = colToUseForX[i];
                      order[newOrder[i]] = -1;
                      i++;
                    }
/* 439 *///          / axis = (originalUnits = order).length;
               // for (td = 0; td < axis; td++)
               // { int colOrder = originalUnits[td];
/* 440 *///          /   if (colOrder != -1) {
/* 441 *///          /     newOrder[i] = colOrder;
/* 442 *///          /     i++;
/*     *///          /   }
/*     *///          / }
                    this.table.setColumnOrder(newOrder);
                  }
                }
              
                private void addVariantLabels(int cols, Variant variant)
                {
                  if (this.titleRows > 1) {
                    TableItem subtitle = this.table.getItem(1);
                    subtitle.setText(cols, variant.getLabel());
                  }
                }
              
                private void populateColumn(int cols, TupleData td, OutputProperties outputProperties, boolean useXCol, int currentColumn)
                {
                  PriorityQueue sorted = td.getSortedDataPoints();
              
                  String xUnits = outputProperties.getXUnits(td);
                  String yUnits = outputProperties.getYUnits(td);
              
                  TableItem[] items = new TableItem[sorted.size()];
              
                  int itemRow = this.titleRows;
                  int k = 0;
                  DataPoint point = (DataPoint)sorted.poll();
                  while (point != null) {
                    String xval = point.formatX(VGCAxes.GC_NUMBER);
              
                    int comp = 1;
                    while ((itemRow < this.table.getItemCount()) && 
                      ((comp = compareTo(xval, this.table.getItem(itemRow)
                      .getText(1))) > 0))
                    {
                      itemRow++;
                    }
                    if (comp == 0)
                    {
                      items[k] = this.table.getItem(itemRow);
                      itemRow++;
                    }
                    else {
                      items[k] = new TableItem(this.table, 0, itemRow);
                      items[k].setText(1, xval);
                      itemRow++;
                    }
                    point = (DataPoint)sorted.poll();
                    k++;
                  }
              
                  PriorityQueue sorted2 = td.getSortedDataPoints();
                  DataPoint point2 = (DataPoint)sorted2.poll();
                  int j = 0;
                  while (point2 != null) {
                    TableItem item = items[j];
              
                    item.setText(0,String.valueOf(point2.getSequenceUID()));
              
                    if (useXCol) {
                      String xValue = point2.formatX(xUnits);
                      item.setText(currentColumn, xValue);
                    }
                    String yValue = point2.formatY(yUnits);
                    item.setText(cols, yValue);
                    j++;
                    point2 = (DataPoint)sorted2.poll();
                  }
                }
              
                private void createYColumn(int cols, Variant variant, TupleData td, OutputProperties outputProperties)
                {
                  String units = outputProperties.getYUnits(td);
              
                  TableColumn column = cols >= this.table.getColumnCount() ? new TableColumn(
                    this.table, 131072) : this.table.getColumn(cols);
                  column.setText(td.getDisplayName());
                  column.setMoveable(true);
                  column.setToolTipText(VARIANT + ": " + variant.getLabel());
                  column.addSelectionListener(new SortingListener());
              
                  TableItem unittitle = this.table.getItem(0);
                  unittitle.setText(cols, units);
                  this.axes.put(td.getDisplayName(), td.getYAxis());
                }
              
                private void addSubheaders(int cols, Variant variant)
                {
                  if (this.titleRows > 1) {
                    TableItem subtitle = this.table.getItem(1);
                    subtitle.setText(1, VARIANT);
                    subtitle.setText(cols, variant.getLabel());
                  }
                }
              
                private String getTableData(Table table)
                {
                  return getTableData(table, table.getItems());
                }
              
                private String getTableData(Table table, TableItem[] items)
                {
                  String separator = new TabbedDataPreferenceHelper()
                    .getSeparator();
                  String newline = System.getProperty("line.separator");
                  StringBuffer buffer = new StringBuffer();
                  int[] colOrder = table.getColumnOrder();
                  TableColumn[] cols = table.getColumns();
                  for (int i = 0; i < colOrder.length; i++) {
                    if (colOrder[i] != 0)
                    {
                      String fieldStr = cols[colOrder[i]].getText();
                      buffer.append(escapeField(fieldStr, separator, newline));
                      if (i < colOrder.length - 1)
                        buffer.append(separator);
                    }
                  }
                  buffer.append(newline);
                  for (int j = 0; j < items.length; j++) {
                    for (int i = 0; i < colOrder.length; i++) {
                      if (colOrder[i] != 0)
                      {
                        String fieldStr = items[j].getText(colOrder[i]);
                        buffer.append(escapeField(fieldStr, separator, newline));
                        if (i < colOrder.length - 1)
                          buffer.append(separator);
                      }
                    }
                    buffer.append(newline);
                  }
                  return buffer.toString();
                }
              
                public static String escapeField(String field, String seperator, String newline)
                {
                  if ((field.indexOf(seperator) >= 0) || (field.indexOf(newline) >= 0) || 
                    (field.indexOf(QUOTE) >= 0)) {
                    return QUOTE + field.replaceAll(QUOTE, DOUBLEQUOTES) + QUOTE;
                  }
                  return field;
                }
              
                private void calculateNumTitleRows()
                {
                  this.titleRows = 1;
              
                  Variant[] variants = this.dataSet.getVariants();
                  if (variants.length > 1)
                    this.titleRows += 1;
                }
              
                public int getSequenceUID(ISelection selection)
                {
                  TableItem[] items = this.table.getSelection();
                  int sequenceUID = -1;
              
                  if ((items != null) && (items.length > 0)) {
                    TableItem item = items[0];
                    try {
                      sequenceUID = Integer.parseInt(item.getText());
                    }
                    catch (NumberFormatException localNumberFormatException)
                    {
                    }
                  }
                  return sequenceUID;
                }
              
                public void goToSequenceUID(int sequenceUID)
                {
                  int index = scanSearch(sequenceUID);
                  if (index > -1) {
                    this.table.select(index);
              
                    TableItem[] selection = this.table.getSelection();
                    if ((selection != null) && (selection.length > 0))
                      this.table.showItem(selection[0]);
                  }
                }
              
                private int scanSearch(int sequenceUID)
                {
                  for (int i = this.titleRows; i < this.table.getItemCount(); i++) {
                    TableItem item = this.table.getItem(i);
                    if (item != null) {
                      try {
                        int UID = Integer.parseInt(item.getText());
                        if (UID == sequenceUID) {
                          return i;
                        }
                      }
                      catch (NumberFormatException localNumberFormatException)
                      {
                      }
                    }
                  }
              
                  return -1;
                }
              
                public void addSelectionListener(SelectionListener listener) {
                  this.table.addSelectionListener(listener);
                }
              
                public ISelectionProvider getSelectionProvider() {
                  return this.selectionProvider;
                }
              
                public void widgetDefaultSelected(SelectionEvent e) {
                  widgetSelected(e);
                }
              
                public void widgetSelected(SelectionEvent e) {
                  if (this.selectionProvider != null)
                  {
                    TableItem[] items = this.table.getSelection();
                    this.selectionProvider.setSelection(new StructuredSelection(
                      this.table.getSelection()));
                    if ((items != null) && (items.length > 0)) {
                      TableItem item = items[0];
                      setSelection(item);
                    }
                  }
                }
              
                public void mouseMove(MouseEvent e) {
                  this.lastMouse = new Point(e.x, e.y);
                  if (this.selectionProvider != null)
                  {
                    TableItem item = this.table.getItem(this.lastMouse);
                    setSelection(item);
                  }
                }
              
                private void setSelection(TableItem item)
                {
                  if (item != null) {
                    int columnCount = this.table.getColumnCount();
              
                    for (int i = 0; i < columnCount; i++) {
                      Rectangle rect = item.getBounds(i);
              
                      if (rect.contains(this.lastMouse))
                      {
                        DataAxis axis = (DataAxis)this.axes.get(
                          this.table.getColumn(i).getText());
                        if (axis != null) {
                          AxisSelection selection = new AxisSelection(axis);
                          this.selectionProvider.setSelection(selection);
                        }
                      }
                    }
                  }
                }
              
                private class SortingListener implements SelectionListener {
                  private SortingListener() {
                  }
              
                  public void widgetDefaultSelected(SelectionEvent ev) { widgetSelected(ev); }
              
              
                  public void widgetSelected(SelectionEvent ev)
                  {
                    TableColumn tc = (TableColumn)ev.getSource();
              
                    Table parentTable = tc.getParent();
                    TableItem[] items = parentTable.getItems();
              
                    TableColumn[] cols = parentTable.getColumns();
                    int i;
                    for (i = 0; i < cols.length; i++) {
                      if (cols[i] == tc)
                        break;
                    }
                    final int colnum = i;
              
                    TableColumn oldSort = parentTable.getSortColumn();
                    final int direction = (oldSort == tc) && (
                      parentTable.getSortDirection() == 128) ? 1024 : 128;
                    parentTable.setSortColumn(tc);
                    parentTable.setSortDirection(direction);
              
                    Comparator c = new Comparator() {
                      public int compare(Object o1, Object o2) {
                        TableItem ti1 = (TableItem)o1;
                        TableItem ti2 = (TableItem)o2;
                        int ret = TabbedDataDisplayer.this
                          .compareTo(ti1.getText(colnum), 
                          ti2.getText(colnum));
              
                        if (ret == 0)
                          ret = TabbedDataDisplayer.this.compareTo(ti1.getText(0), ti2.getText(0));
                        if (direction == 1024)
                          ret = -ret;
                        return ret;
                      }
                    };
                    Arrays.sort(items, TabbedDataDisplayer.this.titleRows, items.length, c);
              
                    String[][] tableText = new String[items.length][
                      parentTable.getColumnCount()];
              
                    for (int row = TabbedDataDisplayer.this.titleRows; row < items.length; row++) {
                      for (int column = 0; column < parentTable.getColumnCount(); column++) {
                        tableText[row][column] = items[row].getText(column);
                      }
                    }
              
                    for (int row = TabbedDataDisplayer.this.titleRows; row < items.length; row++)
                      parentTable.getItem(row).setText(tableText[row]);
                  }
                }
              }

